/*
 * Copyright (c) 2023 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <securec.h>

#include "tee_internal_se_api_mock.h"

TEE_Result TEE_SEServiceGetReaders(TEE_SEServiceHandle seServiceHandle, TEE_SEReaderHandle *seReaderHandleList,
    uint32_t *seReaderHandleListLen)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEServiceGetReaders(seServiceHandle, seReaderHandleList, seReaderHandleListLen);
    }

    return 0;
}

TEE_Result TEE_SEReaderGetName(TEE_SEReaderHandle seReaderHandle, char *readerName, uint32_t *readerNameLen)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEReaderGetName(seReaderHandle, readerName, readerNameLen);
    }
    return 0;
}

TEE_Result TEE_SEReaderOpenSession(TEE_SEReaderHandle seReaderHandle, TEE_SESessionHandle *seSessionHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEReaderOpenSession(seReaderHandle, seSessionHandle);
    }
    return 0;
}

void TEE_SEReaderCloseSessions(TEE_SEReaderHandle seReaderHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEReaderCloseSessions(seReaderHandle);
    }
}

void TEE_SESessionClose(TEE_SESessionHandle seSessionHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SESessionClose(seSessionHandle);
    }
}

void TEE_SESessionCloseChannels(TEE_SESessionHandle seSessionHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SESessionCloseChannels(seSessionHandle);
    }
}

TEE_Result TEE_SEServiceOpen(TEE_SEServiceHandle *seServiceHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEServiceOpen(seServiceHandle);
    }

    return 0;
}

void TEE_SEServiceClose(TEE_SEServiceHandle seServiceHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEServiceClose(seServiceHandle);
    }
}

TEE_Result TEE_SEChannelTransmit(TEE_SEChannelHandle seChannelHandle, void *command, uint32_t command_len,
    void *response, uint32_t *responseLen)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEChannelTransmit(seChannelHandle, command, command_len, response, responseLen);
    }
    return 0;
}

void TEE_SEChannelClose(TEE_SEChannelHandle seChannelHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEChannelClose(seChannelHandle);
    }
}

TEE_Result TEE_SESessionOpenLogicalChannel(TEE_SESessionHandle seSessionHandle, TEE_SEAID *seAid,
    TEE_SEChannelHandle *seChannelHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SESessionOpenLogicalChannel(seSessionHandle, seAid, seChannelHandle);
    }
    return 0;
}

TEE_Result TEE_SESessionOpenBasicChannel(TEE_SESessionHandle seSessionHandle, TEE_SEAID *seAid,
    TEE_SEChannelHandle *seChannelHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SESessionOpenBasicChannel(seSessionHandle, seAid, seChannelHandle);
    }
    return 0;
}

TEE_Result TEE_SEChannelGetSelectResponse(TEE_SEChannelHandle seChannelHandle, void *response, uint32_t *responseLen)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SEChannelGetSelectResponse(seChannelHandle, response, responseLen);
    }
    return 0;
}

TEE_Result TEE_SESecureChannelOpen(TEE_SEChannelHandle seChannelHandle, TEE_SC_Params *params)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SESecureChannelOpen(seChannelHandle, params);
    }
    return 0;
}

void TEE_SESecureChannelClose(TEE_SEChannelHandle seChannelHandle)
{
    if (auto *invoker = OHOS::SeBaseServices::UnitTest::TeeInternalSeApiMock::GetInstance(); invoker) {
        return invoker->TEE_SESecureChannelClose(seChannelHandle);
    }
    return;
}

struct TEE_SEServiceHandleInner {
    std::vector<TEE_SEReaderHandleInner> readerhandles;
};

struct TEE_SEReaderHandleInner {
    std::string name;
    std::vector<std::vector<uint8_t>> aids;
    std::vector<TEE_SESessionHandle> sessions;
};

struct TEE_SESessionHandleInner {
    std::vector<std::vector<uint8_t>> aids;
    std::vector<TEE_SEChannelHandle> channels;
};

struct TEE_SEChannelHandleInner {
    std::vector<uint8_t> aid;
    std::vector<uint8_t> res;
};

namespace OHOS {
namespace SeBaseServices {
namespace UnitTest {

TeeInternalSeApiMock::TeeInternalSeApiMock(const SeConfig &config) : config_(std::move(config))
{
    MockServiceOpenClose();
    MockServiceReaders();
    MockServiceReaderOpenCloseSession();
    MockServiceChannelOpenCloseSession();
}
void TeeInternalSeApiMock::MockServiceOpenClose() const
{
    ON_CALL(*this, TEE_SEServiceOpen).WillByDefault([this](TEE_SEServiceHandle *seServiceHandle) -> TEE_Result {
        auto handle = new TEE_SEServiceHandleInner();
        if (handle == nullptr) {
            return TEE_ERROR_BAD_PARAMETERS;
        }
        for (const auto &[name, aids] : config_) {
            handle->readerhandles.push_back({name, aids, {}});
        }
        *seServiceHandle = handle;
        return 0;
    });

    ON_CALL(*this, TEE_SEServiceClose).WillByDefault([](TEE_SEServiceHandle seServiceHandle) -> void {
        delete seServiceHandle;
    });
}

void TeeInternalSeApiMock::MockServiceReaders() const
{
    ON_CALL(*this, TEE_SEServiceGetReaders)
        .WillByDefault([](TEE_SEServiceHandle seServiceHandle, TEE_SEReaderHandle *seReaderHandleList,
                           uint32_t *seReaderHandleListLen) -> TEE_Result {
            uint32_t size = static_cast<uint32_t>(seServiceHandle->readerhandles.size());
            if (size > *seReaderHandleListLen) {
                return TEE_ERROR_BAD_PARAMETERS;
            }
            for (uint32_t i = 0; i < size; i++) {
                seReaderHandleList[i] = &seServiceHandle->readerhandles[i];
            }
            *seReaderHandleListLen = size;
            return TEE_SUCCESS;
        });

    ON_CALL(*this, TEE_SEReaderGetName)
        .WillByDefault([](TEE_SEReaderHandle seReaderHandle, char *readerName, uint32_t *readerNameLen) -> TEE_Result {
            if (seReaderHandle == nullptr) {
                return TEE_ERROR_BAD_PARAMETERS;
            }
            auto handle = *seReaderHandle;
            std::string name = handle.name;
            if (strcpy_s(readerName, *readerNameLen, name.c_str()) != EOK) {
                *readerNameLen = 0;
                return TEE_ERROR_BAD_PARAMETERS;
            }

            *readerNameLen = static_cast<uint32_t>(name.length());

            return TEE_SUCCESS;
        });
}
void TeeInternalSeApiMock::MockServiceReaderOpenCloseSession() const
{
    ON_CALL(*this, TEE_SEReaderOpenSession)
        .WillByDefault([](TEE_SEReaderHandle seReaderHandle, TEE_SESessionHandle *seSessionHandle) -> TEE_Result {
            if (seReaderHandle == nullptr) {
                return TEE_ERROR_BAD_PARAMETERS;
            }

            *seSessionHandle = new TEE_SESessionHandleInner;
            if (*seSessionHandle == nullptr) {
                return TEE_ERROR_BAD_PARAMETERS;
            }
            (*seSessionHandle)->aids = seReaderHandle->aids;
            seReaderHandle->sessions.push_back(*seSessionHandle);
            return TEE_SUCCESS;
        });

    ON_CALL(*this, TEE_SEReaderCloseSessions).WillByDefault([](TEE_SEReaderHandle seReaderHandle) -> TEE_Result {
        if (seReaderHandle == nullptr) {
            return TEE_ERROR_BAD_PARAMETERS;
        }

        for (auto session : seReaderHandle->sessions) {
            for (auto channel : session->channels) {
                delete channel;
            }
            session->channels.clear();
            delete session;
        }
        seReaderHandle->sessions.clear();
        return TEE_SUCCESS;
    });
}

void TeeInternalSeApiMock::MockServiceChannelOpenCloseSession() const
{
    ON_CALL(*this, TEE_SESessionOpenLogicalChannel)
        .WillByDefault([](TEE_SESessionHandle seSessionHandle, TEE_SEAID *seAid,
                           TEE_SEChannelHandle *seChannelHandle) -> TEE_Result {
            if (seSessionHandle == nullptr || seAid == nullptr || seChannelHandle == nullptr) {
                return TEE_ERROR_BAD_PARAMETERS;
            }
            for (const auto &aid : seSessionHandle->aids) {
                std::vector<uint8_t> curr(seAid->buffer, seAid->buffer + seAid->bufferLen);
                if (curr == aid) {
                    *seChannelHandle = new TEE_SEChannelHandleInner();
                    (*seChannelHandle)->aid = aid;
                    (*seChannelHandle)->res = {0x90, 0x00};
                    seSessionHandle->channels.push_back(*seChannelHandle);
                    return TEE_SUCCESS;
                }
            }
            return TEE_ERROR_BAD_PARAMETERS;
        });

    ON_CALL(*this, TEE_SEChannelGetSelectResponse)
        .WillByDefault([](TEE_SEChannelHandle seChannelHandle, void *response, uint32_t *responseLen) -> TEE_Result {
            if (seChannelHandle == nullptr || response == nullptr || responseLen == nullptr) {
                return TEE_ERROR_BAD_PARAMETERS;
            }
            uint32_t size = static_cast<uint32_t>(seChannelHandle->res.size());
            if ((memcpy_s(response, *responseLen, seChannelHandle->res.data(), size)) != EOK) {
                return TEE_ERROR_BAD_PARAMETERS;
            }
            *responseLen = size;
            return TEE_SUCCESS;
        });

    ON_CALL(*this, TEE_SESessionCloseChannels).WillByDefault([](TEE_SESessionHandle seSessionHandle) -> TEE_Result {
        if (seSessionHandle == nullptr) {
            return TEE_ERROR_BAD_PARAMETERS;
        }
        for (auto channel : seSessionHandle->channels) {
            delete channel;
        }
        seSessionHandle->channels.clear();

        return TEE_SUCCESS;
    });
}
} // namespace UnitTest
} // namespace SeBaseServices
} // namespace OHOS